/********************************************************************
    Rhapsody	: 8.0.4
    Login		: Administrator
    Component	: ServerRun
    Configuration 	: TestConfig
    Model Element	: TestConfig
//!	Generated Date	: Thu, 12, Jul 2018  
    File Path	: ../../ServerRun/main.cpp
*********************************************************************/

#include <oxf.h>
#include <signal.h>
#include "getopt.h"
#include "Utils/MiniDumper.h"
#include "Utils/iLPR.h"
#include "Utils/iLPR_Version.h"
#include "FPM_Server.h"
#include <iostream>
#include <assert.h>

static iLPR_ServerState sServerState = iLPRStartingUpState;
const char *theXMLFilePath = 0;
FPM_Server *FPMServer = 0;

static SERVICE_STATUS_HANDLE sServiceStatusHandle = 0;

static void ReportStatus(DWORD inCurrentState, DWORD inExitCode);

static void InstallService(char *inServiceName);

static void RemoveService(char *inServiceName);

static void RunAsService(char *inServiceName);

void WINAPI ServiceControl(DWORD);

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);

iLPR_ServerState StartServer(bool inDontFork);

void RunServer();

static void signalHandler(int signo) {
    std::cout << "Shutting down" << std::endl;
    sServerState = iLPRShuttingDownState;
}

int main(int argc, char *argv[]) {
    bool notAService = false;
    int ch;
    while ((ch = getopt(argc, argv, "vdc:irsS:I")) != EOF) {
        switch (ch) {
            case 'v':

                printf("%s v = %s \n", "iLPR", iLPR::versionStr);

                printf("usage: %s [ -d | -v | -c /iLPR-Setting.xml |-S numseconds | -I | -h ]\n", "iLPR");
                printf("-d: Don't run as a Win32 Service\n");
                printf("-c c:\\iLPR-Setting.xml: Specify a config file path\n");
                printf("-i: Install the iLPR service\n");
                printf("-r: Remove the iLPR service\n");
                printf("-s: Start the iLPR service\n");
#if 0
            printf("-S n: Display server stats in the console every \"n\" seconds\n");
            printf("-I: ddStart the server in the idle state\n");
#endif
                ::exit(0);
            case 'd':
                notAService = true;
                break;
            case 'c':
                assert(optarg !=
                       NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
                theXMLFilePath = optarg;
                break;
#if 0
            case 'S':
                Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
                sStatsUpdateInterval = ::atoi(optarg);
                break;
#endif
            case 'i':
                printf("Installing the iLPR service...\n");
                ::InstallService("iLPR");
                printf("Starting the iLPR service...\n");
                ::RunAsService("iLPR");
                ::exit(0);
                break;
            case 'r':
                printf("Removing the iLPR service...\n");
                ::RemoveService("iLPR");
                ::exit(0);
            case 's':
                printf("Starting the iLPR service...\n");
                ::RunAsService("iLPR");
                ::exit(0);
#if 0
            case 'I':
                sServerState = qtssIdleState;
                break;
#endif
            default:
                break;
        }
    }

    if (notAService) {

        if (signal(SIGINT, signalHandler) == SIG_ERR) {
            std::cerr << "Couldn't install signal handler for SIGINT" << std::endl;
            exit(-1);
        }
        if (signal(SIGTERM, signalHandler) == SIG_ERR) {
            std::cerr << "Couldn't install signal handler for SIGTERM" << std::endl;
            exit(-1);
        }

        // If we're running off the command-line, don't do the service initiation crap.^M
        sServerState = ::StartServer(false); // No stats update interval for now
        if (sServerState != iLPRFatalErrorState) {
            ::RunServer();
            exit(0);
        }
        exit(-1);
    }

    SERVICE_TABLE_ENTRY dispatchTable[] =
            {
                    {"", ServiceMain},
                    {NULL, NULL}
            };

    // In case someone runs the server improperly, print out a friendly message.
    printf("iLPR must either be started from the DOS Console\n");
    printf("using the -d command-line option, or using the Service Control Manager\n\n");
    printf("Waiting for the Service Control Manager to start iLPR...\n");
    BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable);
    if (!theErr) {
        printf("Fatal Error: Couldn't start Service\n");
        ::exit(-1);
    }
    return 0;
}

void __stdcall ServiceMain(DWORD /*argc*/, LPTSTR *argv) {
    char *theServerName = argv[0];

    sServiceStatusHandle = ::RegisterServiceCtrlHandler(theServerName, &ServiceControl);
    if (sServiceStatusHandle == 0) {
        printf("Failure registering service handler");
        ::exit(-1);
    }

    //
    // Report our status
    ::ReportStatus(SERVICE_START_PENDING, NO_ERROR);

    // Start & Run the server - no stats update interval for now
    sServerState = ::StartServer(false);
    if (sServerState != iLPRFatalErrorState) {
        ::ReportStatus(SERVICE_RUNNING, NO_ERROR);
        ::RunServer(); // This function won't return until the server has died

        //
        // Ok, server is done...
        ::ReportStatus(SERVICE_STOPPED, NO_ERROR);
        exit(0);
    } else {
        ::ReportStatus(SERVICE_STOPPED, ERROR_BAD_COMMAND); // I dunno... report some error
        exit(-1);
    }
}

void WINAPI ServiceControl(DWORD inControlCode) {
    DWORD theStatusReport = SERVICE_START_PENDING;

    switch (inControlCode) {
        // Stop the service.
        //
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN: {
            if (sServerState == iLPRStartingUpState)
                break;

            // Signal the server to shut down.
            sServerState = iLPRShuttingDownState;
            break;
        }
        case SERVICE_CONTROL_PAUSE: {
#if 0
            if (sServerState != RunningState)
                break;

            // Signal the server to refuse new connections.
            theState = qtssRefusingConnectionsState;
            if (theServer != NULL)
                theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
#endif
            break;
        }
        case SERVICE_CONTROL_CONTINUE: {
#if 0
            if (theState != qtssRefusingConnectionsState)
                break;

            // Signal the server to refuse new connections.
            theState = qtssRefusingConnectionsState;
            if (theServer != NULL)
                theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
#endif
            break;
        }
        case SERVICE_CONTROL_INTERROGATE:
            break; // Just update our status
        default:
            break;
    }




    // Convert a QTSS state to a Win32 Service state^M
    switch (sServerState) {
        case iLPRStartingUpState:
            theStatusReport = SERVICE_START_PENDING;
            break;
        case iLPRRunningState:
            theStatusReport = SERVICE_RUNNING;
            break;
        case iLPRRefusingConnectionsState:
            theStatusReport = SERVICE_PAUSED;
            break;
        case iLPRFatalErrorState:
            theStatusReport = SERVICE_STOP_PENDING;
            break;
        case iLPRShuttingDownState:
            theStatusReport = SERVICE_STOP_PENDING;
            break;
        default:
            theStatusReport = SERVICE_RUNNING;
            break;
    }

    printf("Reporting status from ServiceControl function\n");
    ::ReportStatus(theStatusReport, NO_ERROR);
}

void ReportStatus(DWORD inCurrentState, DWORD inExitCode) {
    static bool sFirstTime = true;
    static DWORD sCheckpoint = 0;
    static SERVICE_STATUS sStatus;

    if (sFirstTime) {
        sFirstTime = false;

        // Setup the status structure
        sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
        sStatus.dwCurrentState = SERVICE_START_PENDING;
        sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
        //sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
        sStatus.dwWin32ExitCode = 0;
        sStatus.dwServiceSpecificExitCode = 0;
        sStatus.dwCheckPoint = 0;
        sStatus.dwWaitHint = 0;
    }

    if (sStatus.dwCurrentState == SERVICE_START_PENDING)
        sStatus.dwCheckPoint = ++sCheckpoint;
    else
        sStatus.dwCheckPoint = 0;

    sStatus.dwCurrentState = inCurrentState;
    sStatus.dwServiceSpecificExitCode = inExitCode;
    BOOL theErr = SetServiceStatus(sServiceStatusHandle, &sStatus);
    if (theErr == 0) {
        DWORD theerrvalue = ::GetLastError();
    }
}

void RunAsService(char *inServiceName) {
    SC_HANDLE theService;
    SC_HANDLE theSCManager;

    theSCManager = ::OpenSCManager(
            NULL,                   // machine (NULL == local)
            NULL,                   // database (NULL == default)
            SC_MANAGER_ALL_ACCESS   // access required
    );
    if (!theSCManager)
        return;

    theService = ::OpenService(
            theSCManager,               // SCManager database
            inServiceName,               // name of service
            SERVICE_ALL_ACCESS);

    SERVICE_STATUS lpServiceStatus;

    if (theService) {
        const int kNotRunning = 1062;
        bool stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, &lpServiceStatus);
        if (!stopped && ((int) ::GetLastError() != kNotRunning))
            printf("Stopping Service Error: %d\n", ::GetLastError());

        bool started = ::StartService(theService, 0, NULL);
        if (!started)
            printf("Starting Service Error: %d\n", ::GetLastError());

        ::CloseServiceHandle(theService);
    }

    ::CloseServiceHandle(theSCManager);
}

void InstallService(char *inServiceName) {
    SC_HANDLE theService;
    SC_HANDLE theSCManager;

    TCHAR thePath[512];
    TCHAR theAppBaseName[512];
    TCHAR theQuotedPath[522];

    BOOL theErr = ::GetModuleFileName(NULL, thePath, 512);
    if (!theErr)
        return;

    char *pSlash = strrchr((char *) thePath, '\\');
    if (pSlash) {
        strcpy((char *) theAppBaseName, (const char *) pSlash + 1);
        *(pSlash + 1) = 0;
    }

    sprintf(theQuotedPath, "\"%s%s\" -c \"%s%s\"", thePath, theAppBaseName, thePath, "iLPR-Setting.xml");

    theSCManager = ::OpenSCManager(
            NULL,                   // machine (NULL == local)
            NULL,                   // database (NULL == default)
            SC_MANAGER_ALL_ACCESS   // access required
    );
    if (!theSCManager) {
        printf("Failed to install iLPR Service\n");
        return;
    }

    theService = CreateService(
            theSCManager,               // SCManager database
            inServiceName,               // name of service
            inServiceName,               // name to display
            SERVICE_ALL_ACCESS,         // desired access
            SERVICE_WIN32_OWN_PROCESS,  // service type
            SERVICE_AUTO_START,       // start type
            SERVICE_ERROR_NORMAL,       // error control type
            theQuotedPath,               // service's binary
            NULL,                       // no load ordering group
            NULL,                       // no tag identifier
            NULL,       // dependencies
            NULL,                       // LocalSystem account
            NULL);                      // no password

    if (theService) {
        SERVICE_DESCRIPTION desc;
        desc.lpDescription = "�豸��������";
        if (ChangeServiceConfig2(theService, SERVICE_CONFIG_DESCRIPTION, &desc)) {
            printf("Added iLPR Service Description\n");
        }

        SERVICE_DELAYED_AUTO_START_INFO info = {true};

        if (ChangeServiceConfig2(theService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &info)) {
            printf("Added iLPR Service Delayed Auto Start\n");
        }

        SERVICE_FAILURE_ACTIONS failact = {0};
        SC_ACTION act[3];
        act[0].Delay = act[1].Delay = act[2].Delay = 1 * 1000;
        act[0].Type = act[1].Type = act[2].Type = SC_ACTION_RESTART;
        failact.cActions = 3;
        failact.lpsaActions = act;
        failact.dwResetPeriod = 0;
        if (ChangeServiceConfig2(theService, SERVICE_CONFIG_FAILURE_ACTIONS, &failact)) {
            printf("Seted iLPR Service failure actions\n");
        }
        ::CloseServiceHandle(theService);
        printf("Installed iLPR Service\n");
    } else
        printf("Failed to install iLPR Service\n");

    ::CloseServiceHandle(theSCManager);
}

void RemoveService(char *inServiceName) {
    SC_HANDLE theSCManager;
    SC_HANDLE theService;
    SERVICE_STATUS sStatus;

    theSCManager = ::OpenSCManager(
            NULL,                   // machine (NULL == local)
            NULL,                   // database (NULL == default)
            SC_MANAGER_ALL_ACCESS   // access required
    );
    if (!theSCManager) {
        printf("Failed to remove iLPR Service\n");
        return;
    }

    theService = ::OpenService(theSCManager, inServiceName, SERVICE_ALL_ACCESS);
    if (theService) {
        if (::ControlService(theService, SERVICE_CONTROL_STOP, &sStatus)) {
            printf("Stopping Service: %s", inServiceName);
            Sleep(1000);

            while (::QueryServiceStatus(theService, &sStatus)) {
                if (SERVICE_STOP_PENDING == sStatus.dwCurrentState) {
                    printf(".");
                    Sleep(1000);
                } else
                    break;
            }
            if (SERVICE_STOPPED == sStatus.dwCurrentState)
                printf("%s stopped.\n", inServiceName);
            else
                printf("%s failed to stopp.\n", inServiceName);
        }
        if (::DeleteService(theService))
            printf("Removed iLPR Service!\n");
        else
            printf("Remove iLPR Service failed!\n");
        ::CloseServiceHandle(theService);
    } else
        printf("Failed to remove iLPR Service\n");
    ::CloseServiceHandle(theSCManager);
}

iLPR_ServerState StartServer(bool inDontFork) {
    bool doneStartingUp = false;
    iLPR_ServerState theServerState = iLPRStartingUpState;
    if (OXF::Instance().Initialize()) {
        FPMServer = new FPM_Server();
        FPMServer->init();
        if (!FPMServer->run()) {
            printf("Failed to start iLPR, exiting...\n");
            delete FPMServer;
            FPMServer = 0;
            theServerState = iLPRFatalErrorState;
        } else {
            doneStartingUp = true;
            theServerState = iLPRRunningState;
            printf("iLPR done starting up\n");
        }
    } else
        theServerState = iLPRFatalErrorState;
    return theServerState;
}

void RunServer() {

#ifdef WIN32
    CMiniDumper _miniDumper(true);
#endif

    while ((sServerState != iLPRShuttingDownState) &&
           (sServerState != iLPRFatalErrorState)) {
#ifdef WIN32
        Sleep(1000);
#else
        usleep(1000*1000);
#endif
    }
    if (FPMServer)
        delete FPMServer;
    FPMServer = 0;
}
/*********************************************************************
  File Path	: ../../RunServer/main.cpp
 *********************************************************************/
